using System.Collections;
using System.Net.Http.Json;
using System.Text;
using System.Text.Json;
using Blazored.LocalStorage;
using Microsoft.AspNetCore.Components;
using StudentVideo.Client.Common.Constants;
using StudentVideo.Shared.Models;
using StudentVideo.Shared.Dtos.Login;

namespace StudentVideo.Client.Handler;

/// <summary>
/// 自定义 HTTP 请求
/// </summary>
/// <param name="httpClient"></param>
/// <param name="messageService"></param>
/// <param name="localStorage"></param>
public class HttpHandler(HttpClient httpClient, ILocalStorageService localStorage, NavigationManager navigation) {
   private readonly HttpClient _httpClient = httpClient;
   private readonly ILocalStorageService _localStorage = localStorage;
   private readonly NavigationManager _navigation = navigation;

   /// <summary>
   /// uri 前缀
   /// </summary>
   private const string Prefix = "/api";

   public Task<HttpResult<T>> PostAsync<T>(string endpoint, object? data) where T : class
       => RequestAsync<T>(HttpMethod.Post, endpoint, data);

   public Task<HttpResult> PostAsync(string endpoint, object? data)
       => RequestAsync(HttpMethod.Post, endpoint, data);

   public Task<HttpResult<T>> GetAsync<T>(string endpoint, object? data = null) where T : class
       => RequestAsync<T>(HttpMethod.Get, endpoint, data);

   public Task<HttpResult> GetAsync(string endpoint, object? data = null)
       => RequestAsync(HttpMethod.Get, endpoint, data);

   private async Task<HttpResult<T>> RequestAsync<T>(HttpMethod method, string url) where T : class {
      return await RequestAsync<T>(method, url, null);
   }

   private async Task<HttpResult<T>> RequestAsync<T>(HttpMethod method, string url, object? data) where T : class {
      url = $"{Prefix}{url}";
      var request = new HttpRequestMessage(method, method == HttpMethod.Get ? BuildQueryUrl(url, data) : url);

      if (method != HttpMethod.Get && data != null) {
         request.Content = new StringContent(JsonSerializer.Serialize(data), Encoding.UTF8, "application/json");
      }

      var response = await _httpClient.SendAsync(request);
      return await HandleResponseAsync<T>(response, url);
   }

   private async Task<HttpResult> RequestAsync(HttpMethod method, string url, object? data) {
      var result = await RequestAsync<object>(method, url, data);
      return new HttpResult {
         StatusCode = result.StatusCode,
         Message = result.Message,
         Timestamp = result.Timestamp,
         Data = result.Data
      };
   }

   private static string BuildQueryUrl(string url, object? data) {
      if (data == null)
         return url;

      var query = new StringBuilder();
      foreach (var prop in data.GetType().GetProperties()) {
         var value = prop.GetValue(data);
         if (value == null)
            continue;

         if (value is IEnumerable enumerable && value is not string) {
            foreach (var item in enumerable) {
               AppendQueryParam(query, prop.Name, item?.ToString());
            }
         } else {
            AppendQueryParam(query, prop.Name, value.ToString());
         }
      }

      return query.Length > 0 ? $"{url}?{query}" : url;
   }

   private static void AppendQueryParam(StringBuilder builder, string name, string? value) {
      if (string.IsNullOrEmpty(value))
         return;
      if (builder.Length > 0)
         builder.Append('&');
      builder.Append(Uri.EscapeDataString(name)).Append('=').Append(Uri.EscapeDataString(value));
   }

   private async Task<HttpResult<T>> HandleResponseAsync<T>(HttpResponseMessage response, string url) where T : class {
      if (!response.IsSuccessStatusCode) {
         // await _message.Error($"请求失败: {url}");
         return new HttpResult<T>();
      }

      HttpResult<T> result = await response.Content.ReadFromJsonAsync<HttpResult<T>>() ?? new HttpResult<T>();
      if (result is null) {
         // await _message.Error($"请求失败: {url}");

         return new HttpResult<T>();
      }

      //  401 错误时，刷新 token
      if (result?.StatusCode == 401 && url != $"{Prefix}/login/reloadtoken") {
         bool reloadTokenResult = await ReloadUserToken();

         if (url != $"{Prefix}/login/reloadtoken") {
            _navigation.NavigateTo(PagePath.Login, true);
            return new HttpResult<T>();
         }

         if (reloadTokenResult) {
            return await HandleResponseAsync<T>(response, url);
         } else {
            _navigation.NavigateTo(PagePath.Login, true);
         }
      }

      if (result?.StatusCode != 200) {
         // await _message.Error(result?.Message ?? $"请求失败: {url}");
      }

      return result ?? new HttpResult<T>();
   }

   /// <summary>
   /// 刷新用户 Token
   /// </summary>
   /// <returns></returns>
   private async Task<bool> ReloadUserToken() {
      HttpResult<LoginResult> result = await RequestAsync<LoginResult>(HttpMethod.Post, "/login/reloadtoken");

      if (result.StatusCode == 200 && result.Data != null) {
         await _localStorage.SetItemAsync(LocalStorageKeys.Token, result.Data.Token);

         return true;
      }
      return false;
   }
}
